package api

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"io"
	"monitor_serve/config"
	"monitor_serve/meta"
	"monitor_serve/mq"
	"monitor_serve/service"
	ossaliyun "monitor_serve/store/oss"

	"monitor_serve/util"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

type FileSimpleApi struct{}

// 传入文件，接受文件流信息放到暂时文件夹tmp里，最后传输完成之后，放到Data文件夹里。
func (f *FileSimpleApi) UploadHandler(c *gin.Context) {
	// 从表单中获取文件
	fileHeader, err := c.FormFile("file")
	username := c.PostForm("username")
	if err != nil {
		fmt.Printf("Failed to get data, err:%s\n", err.Error())
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get file"})
		return
	}

	file, err := fileHeader.Open()
	if err != nil {
		fmt.Printf("Failed to open file, err:%s\n", err.Error())
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
		return
	}
	defer file.Close()

	// 获取文件名和文件路径
	fileName := fileHeader.Filename
	tempDir := "Temp2"
	newFilePath := filepath.Join(tempDir, fmt.Sprintf("%s", fileName))
	// 创建新文件
	newFile, err := os.Create(newFilePath)
	if err != nil {
		fmt.Printf("Failed to create file, err:%s\n", err.Error())
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create file"})
		return
	}
	defer newFile.Close()

	// 打开文件以便读取
	srcFile, err := fileHeader.Open()
	if err != nil {
		fmt.Printf("Failed to open file, err:%s\n", err.Error())
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
		return
	}
	defer srcFile.Close()

	// 复制文件内容
	fileSize, err := io.Copy(newFile, srcFile)
	if err != nil {
		fmt.Printf("Failed to save data into file, err:%s\n", err.Error())
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save data into file"})
		return
	}

	// 计算文件的SHA1值
	newFile.Seek(0, 0)
	sha1 := util.FileSha1(newFile)

	// 创建文件元数据
	fileMeta := meta.FileMeta{
		FileName: fileName,
		Location: newFilePath,
		FileSize: fileSize,
		UploadAt: time.Now().Format("2006-01-02 15:04:05"),
		FileSha1: sha1,
	}

	// 更新文件元数据
	meta.UpdateFileMetaDB(fileMeta)
	//写入ceph
	//写入oss
	ossPath := "oss/" + fileMeta.FileSha1 + fileMeta.FileName
	//err = ossaliyun.GetBucket().PutObject(ossPath, srcFile)
	//if err != nil {
	//	fmt.Println(err)
	//}
	//fileMeta.Location = ossPath
	//消息队列
	data := mq.TransferData{
		FileHash:      fileMeta.FileSha1,
		CurLocation:   fileMeta.Location,
		DestLocation:  ossPath,
		DestStoreType: "oss",
	}
	fmt.Println("data", data)
	pubData, _ := json.Marshal(data)
	suc := mq.Publish(config.RabbitMQ.TransExchangeName, config.RabbitMQ.TransOSSRoutingKey, pubData)
	if !suc {
		//todo 加入重试发送消息逻辑
		fmt.Println("suc", suc)
	}
	//插入用户数据

	err = service.ServiceGroupApp.FileService.UserFileFinished(username, fileMeta.FileSha1, fileMeta.FileName, fileMeta.FileSize)
	if err != nil {
		// 处理错误
		fmt.Println(err)
	}
	// 返回成功响应
	c.JSON(http.StatusOK, gin.H{"message": "File uploaded successfully", "fileMeta": fileMeta})
}

// 下载文件
func (f *FileSimpleApi) DownloadHandler(c *gin.Context) {
	// 从查询参数中获取filehash
	filehash := c.Query("filehash")

	// 检查filehash是否为空
	if filehash == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing filehash parameter"})
		return
	}

	// 调用GetFileMeta方法获取文件元数据
	fm, err := service.ServiceGroupApp.FileService.GetFileMeta(filehash)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": err})
		return

	}
	fmt.Println("fm:", fm)
	// 打开文件
	file, err := os.Open(fm.Location)
	fmt.Println(fm.Location)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
		return
	}
	defer file.Close()
	c.Header("Content-Description", "File Transfer")
	c.Header("Content-Transfer-Encoding", "binary")
	c.Header("Content-Disposition", "attachment; filename="+fm.FileName)
	c.Header("Content-Type", "application/octet-stream")

	c.File(fm.Location)
}

// 删除文件
func (f *FileSimpleApi) DeleteHandler(c *gin.Context) {
	// 从查询参数中获取filehash
	fileSha1 := c.Query("filehash")

	// 检查fileSha1是否为空
	if fileSha1 == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing filehash parameter"})
		return
	}

	// 获取文件元数据以获取文件位置
	fMeta := meta.GetFileMeta(fileSha1)

	// 删除本地文件
	err := os.Remove(fMeta.Location)

	if err != nil {
		fmt.Println(err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete file"})
		return
	}
	// 删除文件元数据
	meta.RemoveFileMeta(fileSha1)
	// 返回成功状态
	c.JSON(http.StatusOK, gin.H{"message": "File deleted successfully"})
}

// 查询用户拥有的文件
func (f *FileSimpleApi) QueryFileByUsername(c *gin.Context) {
	// 从查询参数中获取用户名
	username := c.Query("username")
	if username == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "username parameter is required"})
		return
	}

	// 从查询参数中获取限制数量，如果没有提供，则默认为一个合理的值，例如10
	limit := c.DefaultQuery("limit", "10")
	limitInt, err := strconv.Atoi(limit)
	fmt.Println(limitInt)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid limit parameter"})
		return
	}
	userFiles, err := service.ServiceGroupApp.FileService.QueryUserFileMetas(username)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query user files"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"fileData": userFiles})
}

// 分页查询用户的文件
func (f *FileSimpleApi) QueryFileByUsernamePage(c *gin.Context) {
	username := c.Query("username")
	if username == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "username parameter is required"})
		return
	}
	// 从查询参数中获取页码，默认为1
	pageStr := c.DefaultQuery("page", "1")
	page, err := strconv.Atoi(pageStr)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid page parameter"})
		return
	}
	//每页的数量
	pageSize := 10
	//计算偏移量
	offset := (page - 1) * pageSize
	userFiles, err := service.ServiceGroupApp.FileService.QueryUserFileByName(username, offset, pageSize)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query user files", "details": err.Error()})
		return
	}
	userFiles2, err := service.ServiceGroupApp.FileService.QueryUserFileMetas(username)
	// 返回查询结果
	c.JSON(http.StatusOK, gin.H{
		"data":     userFiles,
		"total":    len(userFiles2), // 如果需要总记录数，可能需要额外查询
		"page":     page,
		"pageSize": pageSize,
	})
}

// 秒传
func (f *FileSimpleApi) TryFastUploadHandler(c *gin.Context) {
	//1.解析请求参数
	fileHash := c.PostForm("fileHash")
	fileName := c.PostForm("fileName")
	userName := c.PostForm("userName")
	fileSizeStr := c.PostForm("fileSize")
	fileSize, err := strconv.ParseInt(fileSizeStr, 10, 64)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid file size"})
		return
	}
	if fileHash == "" || fileName == "" || userName == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing required parameters"})
		return
	}
	//2.从文件表中查询是否又相同的hash文件记录
	fileMeta, err := service.ServiceGroupApp.FileService.GetFileMeta(fileHash)
	//3.查找不到则返回秒传失败
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "File not found, fast upload failed"})
		return
	}
	if fileMeta == nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "File not found, fast upload failed"})
		return
	}
	//4.查找到则说明上传过，则将文件信息写入用户文件表。
	err = service.ServiceGroupApp.FileService.UserFileFinished(userName, fileHash, fileName, fileSize)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "写入失败，秒传失败！"})
	}
	c.JSON(http.StatusOK, gin.H{"message": "Fast upload successful", "fileInfo": fileName})
}

// 断点下载
func (f *FileSimpleApi) DownloadBreakPointHandler(c *gin.Context) {
	// 从查询参数中获取 filehash
	filehash := c.Query("filehash")

	// 检查 filehash 是否为空
	if filehash == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Missing filehash parameter"})
		return
	}

	// 调用 GetFileMeta 方法获取文件元数据
	fm := meta.GetFileMeta(filehash)

	// 打开文件
	file, err := os.Open(fm.Location)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to open file"})
		return
	}
	defer file.Close()

	// 获取文件大小
	fileInfo, err := file.Stat()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get file info"})
		return
	}
	fileSize := fileInfo.Size()

	// 处理 Range 请求头
	rangeHeader := c.Request.Header.Get("Range")
	start, end := int64(0), fileSize-1
	if rangeHeader != "" {
		parts := strings.SplitN(rangeHeader, "=", 2)
		if len(parts) == 2 && parts[0] == "bytes" {
			rangeParts := strings.SplitN(parts[1], "-", 2)
			if len(rangeParts) == 2 {
				start, _ = strconv.ParseInt(rangeParts[0], 10, 64)
				if rangeParts[1] != "" {
					end, _ = strconv.ParseInt(rangeParts[1], 10, 64)
				}
			}
		}
	}
	// 检查范围是否有效
	if start > end || start >= fileSize {
		c.JSON(http.StatusRequestedRangeNotSatisfiable, gin.H{"error": "Invalid range"})
		return
	}

	// 设置响应头
	c.Header("Content-Range", fmt.Sprintf("bytes %d-%d/%d", start, end, fileSize))
	c.Header("Accept-Ranges", "bytes")
	c.Header("Content-Length", fmt.Sprintf("%d", end-start+1))
	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename="+fm.FileName)

	// 设置响应状态码
	if rangeHeader != "" {
		c.Status(http.StatusPartialContent)
	} else {
		c.Status(http.StatusOK)
	}

	// 读取文件的部分内容并发送给客户端
	file.Seek(start, 0)
	c.Stream(func(w io.Writer) bool {
		buffer := make([]byte, 1024*10) // 10KB 缓冲区
		for {
			n, err := file.Read(buffer)
			if err != nil && err != io.EOF {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read file"})
				return false
			}
			if n == 0 {
				break
			}
			w.Write(buffer[:n])
			if n < len(buffer) {
				break
			}
		}
		return true
	})
}

// 生成文件的下载地址
func (f *FileSimpleApi) DownloadUrlHandler(c *gin.Context) {
	var params struct {
		FileHash string `form:"fileHash"`
	}
	err := c.ShouldBind(&params)
	if err != nil {
		fmt.Println(err)
	}
	//从文件表查找记录
	fileMeta, err := service.ServiceGroupApp.FileService.GetFileMeta(params.FileHash)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get file meta"})
		fmt.Println("Error getting file meta:", err)
		return
	}
	//Todo 判断是否在oss还是ceph
	signUrl := ossaliyun.DownloadUrl(fileMeta.Location)
	c.JSON(http.StatusOK, gin.H{"downloadUrl": signUrl})

}
